import * as THREE from "three";
import {OrbitControls} from "three/examples/jsm/controls/OrbitControls.js";
import {Pane} from "tweakpane";
import * as essentials from "@tweakpane/plugin-essentials";
import {EffectComposer} from "three/examples/jsm/postprocessing/EffectComposer.js";
import {RenderPass} from "three/examples/jsm/postprocessing/RenderPass.js";
import {UnrealBloomPass} from "three/examples/jsm/postprocessing/UnrealBloomPass.js";
import {vs, fs} from "./shader.js"

class IntersectionMaterial extends THREE.ShaderMaterial {
    constructor(camera, renderer, url) {
        let noiseTexture = (new THREE.TextureLoader).load(url);
        noiseTexture.wrapS = noiseTexture.wrapT = THREE.RepeatWrapping;
        let pixelRatio = renderer.getPixelRatio();
        let params = {
            time: {
                value: 0
            },
            noiseTexture: {
                value: noiseTexture
            },
            depthTexture: {
                value: null
            },
            cameraNear: {
                value: camera.near
            },
            cameraFar: {
                value: camera.far
            },
            resolution: {
                value: new THREE.Vector2(window.innerWidth * pixelRatio, window.innerHeight * pixelRatio)
            },
            intersectionColor: {
                value: new THREE.Color(1, 1, 0)
            },
            hexPatternColor: {
                value: new THREE.Color(1, 0, 0)
            },
            rimColor: {
                value: new THREE.Color(0, 0, 1)
            }
        };
        super({
            defines: {
                DEPTH_PACKING: 0,
                ORTHOGRAPHIC_CAMERA: 0
            },
            uniforms: THREE.UniformsUtils.merge([THREE.UniformsLib.fog, params]),
            vertexShader: vs,
            fragmentShader: fs,
            fog: true
        });
        this.camera = camera;
        this.renderer = renderer;
        this.pixelRatio = pixelRatio;
        this.initRenderTarget();
        this.initDepthMaterial();
    }

    initRenderTarget() {
        let flag = !!this.renderer.extensions.get("WEBGL_depth_texture");
        this.renderTarget = new THREE.WebGLRenderTarget(window.innerWidth * this.pixelRatio, window.innerHeight * this.pixelRatio);
        this.renderTarget.texture.minFilter = THREE.NearestFilter;
        this.renderTarget.texture.magFilter = THREE.NearestFilter;
        this.renderTarget.texture.generateMipmaps = false;
        this.renderTarget.stencilBuffer = false;
        if (flag) {
            this.renderTarget.depthTexture = new THREE.DepthTexture;
            this.renderTarget.depthTexture.type = THREE.UnsignedShortType;
            this.renderTarget.depthTexture.minFilter = THREE.NearestFilter;
            this.renderTarget.depthTexture.maxFilter = THREE.NearestFilter;
        }
        this.uniforms.depthTexture.value = this.renderTarget.depthTexture || this.renderTarget.texture;
        this.defines.DEPTH_PACKING = this.renderTarget.depthTexture ? 0 : 1;
    }

    initDepthMaterial() {
        this.depthMaterial = new THREE.MeshDepthMaterial;
        this.depthMaterial.depthPacking = THREE.RGBADepthPacking;
        this.depthMaterial.blending = THREE.NoBlending;
    }

    handleResize() {
        this.pixelRatio = this.renderer.getPixelRatio();
        this.uniforms.resolution.value.set(window.innerWidth * this.pixelRatio, window.innerHeight * this.pixelRatio);
        this.renderTarget.setSize(window.innerWidth * this.pixelRatio, window.innerHeight * this.pixelRatio);
    }

    updateUniforms(time) {
        this.uniforms.time.value = time;
    }

    updateColors(e, r, t) {
        this.uniforms.intersectionColor.value.set(e);
        this.uniforms.hexPatternColor.value.set(r);
        this.uniforms.rimColor.value.set(t);
    }

    performDepthPass(scene, camera) {
        scene.overrideMaterial = this.depthMaterial;
        this.renderer.setRenderTarget(this.renderTarget);
        this.renderer.render(scene, camera);
        this.renderer.setRenderTarget(null);
        scene.overrideMaterial = null;
    }
}

let pane = new Pane();
pane.registerPlugin(essentials);
let fpsGraph = pane.addBlade({
    view: "fpsgraph",
    label: "fpsgraph"
});
const canvas = document.querySelector("canvas.webgl");
const scene = new THREE.Scene;
scene.background = new THREE.Color(1984606);
let sizes = {
    width: window.innerWidth,
    height: window.innerHeight
};
window.addEventListener("resize", (() => {
        sizes.width = window.innerWidth;
        sizes.height = window.innerHeight;
        camera.aspect = sizes.width / sizes.height;
        camera.updateProjectionMatrix();
        renderer.setSize(sizes.width, sizes.height);
        renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
        shadermaterial.handleResize();
        composer.setSize(sizes.width, sizes.height);
    }
));

const ambientLight = new THREE.AmbientLight(13421772, .4);
scene.add(ambientLight);

const directionalLight = new THREE.DirectionalLight(16777215, .8);
directionalLight.position.set(0, 5, 5);
scene.add(directionalLight);

const camera = new THREE.PerspectiveCamera(75, sizes.width / sizes.height, .1, 100);
camera.position.set(0, 7, 10);
scene.add(camera);

const controls = new OrbitControls(camera, canvas);
controls.enableDamping = true;

const renderer = new THREE.WebGLRenderer({
    canvas: canvas,
    antialias: true
});
renderer.setSize(sizes.width, sizes.height);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.setClearColor(1578028, 1);
renderer.outputColorSpace = THREE.LinearSRGBColorSpace;

const composer = new EffectComposer(renderer);
const renderPass = new RenderPass(scene, camera);
composer.addPass(renderPass);

const bloomPass = new UnrealBloomPass(new THREE.Vector2(sizes.width, sizes.height), 1.5, .4, .85);
composer.addPass(bloomPass);

const groundTexture = (new THREE.TextureLoader).load("black.png");
groundTexture.wrapS = THREE.RepeatWrapping;
groundTexture.wrapT = THREE.RepeatWrapping;
groundTexture.repeat.set(4, 4);

const planeGeometry = new THREE.PlaneGeometry(50, 50);
const planeMaterial = new THREE.MeshStandardMaterial({
    map: groundTexture
});
const plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.rotation.x = -Math.PI / 2;
scene.add(plane);//背景图

const cubeGeometry = new THREE.BoxGeometry(2, 1, 8);
const cubeMaterial = new THREE.MeshStandardMaterial({
    color: 16711680
});
const cube = new THREE.Mesh(cubeGeometry, cubeMaterial);

function createBlock(width, height, deep, color, x, y, z) {
    const geometry = new THREE.BoxGeometry(width, height, deep);
    const material = new THREE.MeshBasicMaterial({
        color: color
    });
    const mesh = new THREE.Mesh(geometry, material);
    mesh.position.set(x, y, z);
    return mesh;
}

cube.position.set(Math.random() - .5, .1, Math.random() - .5);
// scene.add(cube);
const blocks = [{
    w: 2,
    h: 2,
    d: 2,
    color: 16711680,
    x: -4,
    y: 1,
    z: 0
}, {
    w: 2,
    h: 2,
    d: 2,
    color: 65280,
    x: 0,
    y: 1,
    z: 0
}, {
    w: 2,
    h: 2,
    d: 2,
    color: 255,
    x: 4,
    y: 1,
    z: 0
}, {
    w: 6,
    h: 1,
    d: 6,
    color: 16711935,
    x: -4,
    y: 1,
    z: -8
}, {
    w: 4,
    h: 4,
    d: 4,
    color: 16772778,
    x: 4,
    y: 2,
    z: -8
}, {
    w: 2,
    h: 1,
    d: 4,
    color: 65535,
    x: -4,
    y: .5,
    z: 8
}];
blocks.forEach((e => {
        let mesh = createBlock(e.w, e.h, e.d, e.color, e.x, e.y, e.z);
        // scene.add(mesh)
    }
));
renderer.alpha = true;

const shadermaterial = new IntersectionMaterial(camera, renderer, "hex.png");
shadermaterial.transparent = true;
shadermaterial.blending = THREE.NormalBlending;
shadermaterial.side = THREE.DoubleSide;
shadermaterial.depthWrite = false;

const sphereGeometry = new THREE.SphereGeometry(1.5, 64, 64);
const sphere = new THREE.Mesh(sphereGeometry, shadermaterial);
sphere.position.y = .6;
scene.add(sphere);
console.clear();

const params = {
    intersectionColor: {
        r: 255,
        g: 255,
        b: 0
    },
    hexPatternColor: {
        r: 255,
        g: 0,
        b: 0
    },
    rimColor: {
        r: 0,
        g: 0,
        b: 255
    },
    bloomStrength: 1.5,
    bloomThreshold: .85,
    bloomRadius: .4
};
pane.addBinding(params, "intersectionColor").on("change", (r => {
        let {r: t, g: n, b: a} = r.value;
        shadermaterial.updateColors(new THREE.Color(t / 255, n / 255, a / 255), shadermaterial.uniforms.hexPatternColor.value, shadermaterial.uniforms.rimColor.value)
    }
));
pane.addBinding(params, "hexPatternColor").on("change", (r => {
        let {r: t, g: n, b: a} = r.value;
        shadermaterial.updateColors(shadermaterial.uniforms.intersectionColor.value, new THREE.Color(t / 255, n / 255, a / 255), shadermaterial.uniforms.rimColor.value)
    }
));
pane.addBinding(params, "rimColor").on("change", (r => {
        let {r: t, g: n, b: a} = r.value;
        shadermaterial.updateColors(shadermaterial.uniforms.intersectionColor.value, shadermaterial.uniforms.hexPatternColor.value, new THREE.Color(t / 255, n / 255, a / 255))
    }
));

const clock = new THREE.Clock();
let lastElapsedTime = 0;
const animate = () => {
    fpsGraph.begin();
    let e = clock.getElapsedTime();
    lastElapsedTime = e;
    sphere.scale.setScalar(Math.sin(e)*0.1 + 2);
    // sphere.scale.setScalar(Math.sin(e) + 2);
     // 减少护罩的大小变化
//   sphere.scale.setScalar(2); // 调整护罩缩放范围
    sphere.visible = false;
    shadermaterial.performDepthPass(scene, camera);
    sphere.visible = true;
    shadermaterial.updateUniforms(e);
    controls.update();
    composer.render();
    fpsGraph.end();
    window.requestAnimationFrame(animate)
};
animate();
